package parser

import (
	"cloudDevice2/sampleapi/protobuf"
	"cloudDevice2/sampleapi/utils"
	"fmt"
	"log"
	"math"

	"github.com/golang/protobuf/proto"
)

func ProceedHistoryData(pbData []byte) {
	hisNotify := &protobuf.HisNotification{}
	err := proto.Unmarshal(pbData, hisNotify)
	if err != nil {
		log.Println(fmt.Sprintf("parse 80 healh history error:%s", err.Error()))
		return
	}

	switch hisNotify.GetData().(type) {
	case (*protobuf.HisNotification_HisData):
		hisData := hisNotify.GetHisData()
		log.Println(fmt.Sprintf("seq: %d", hisData.GetSeq()))
		switch hisNotify.GetType() {
		case protobuf.HisDataType(protobuf.HisDataType_HEALTH_DATA):
			hisHealth := hisData.GetHealth()
			if hisHealth != nil {
				parseHealthData(hisHealth)
			}
			break
		case protobuf.HisDataType(protobuf.HisDataType_ECG_DATA):
			hisEcg := hisData.GetEcg()
			if hisEcg != nil {
				//ecg raw data
				parseEcgData(hisEcg)
			}
			break
		case protobuf.HisDataType(protobuf.HisDataType_RRI_DATA):
			hisRri := hisData.GetRri()
			if hisRri != nil {
				//rri/af raw data
				parseRriData(hisRri)
			}
			break
		case protobuf.HisDataType(protobuf.HisDataType_SPO2_DATA):
			hisSpo2 := hisData.GetSpo2()
			if hisSpo2 != nil {
				//continuous spo2 data
				parseSpo2Data(hisSpo2)
			}
			break
		case protobuf.HisDataType(protobuf.HisDataType_THIRDPARTY_DATA):
			hisThirdParty := hisData.GetThirdPartyData()
			if hisThirdParty != nil {
				//our watch intergrate other 3rd party devices, the data
				//of those devices will be upload as 3rd party data
				parseThirdPartyData(hisThirdParty)
			}
			break
		case protobuf.HisDataType(protobuf.HisDataType_PPG_DATA):
			hisPpg := hisData.GetPpg()
			if hisPpg != nil {
				//ppg data
				parsePpgData(hisPpg)
			}
			break
		case protobuf.HisDataType(protobuf.HisDataType_ACCELEROMETER_DATA):
			hisAcc := hisData.GetACCelerometerData()
			if hisAcc != nil {
				//gsensor data
				parseAccData(hisAcc)
			}
			break
		case protobuf.HisDataType(protobuf.HisDataType_MULTI_LEADS_ECG_DATA):
			hisMultiLeadsEcg := hisData.GetMultiLeadsECG()
			if hisMultiLeadsEcg != nil {
				//gsensor data
				parseMultiLeadsEcgData(hisMultiLeadsEcg)
			}
			break
		}

	}
}

func parseHealthData(hisHealthData *protobuf.HisDataHealth) {
	seconds := int64(hisHealthData.GetTimeStamp().GetDateTime().GetSeconds())
	time_str := utils.ParsePbDateTime(seconds)

	//step data
	pedoData := hisHealthData.GetPedoData()
	if pedoData != nil {
		step := pedoData.GetStep()
		distance := float32(pedoData.GetDistance()) * 0.1
		calorie := float32(pedoData.GetCalorie()) * 0.1
		log.Println(fmt.Sprintf("----%s step:%d,distance:%f,calorie:%f", time_str,
			step, distance, calorie))
	}

	//heart rate data
	hrData := hisHealthData.GetHrData()
	if hrData != nil {
		avg_hr := hrData.GetAvgBpm()
		max_hr := hrData.GetMaxBpm()
		min_hr := hrData.GetMinBpm()
		log.Println(fmt.Sprintf("----%s avg hr:%d,max hr:%d,min hr:%d", time_str,
			avg_hr, max_hr, min_hr))
	}

	//spo2 data
	boxyData := hisHealthData.GetBxoyData()
	if boxyData != nil {
		avg_boxy := boxyData.GetAgvOxy()
		max_boxy := boxyData.GetMaxOxy()
		min_boxy := boxyData.GetMinOxy()
		log.Println(fmt.Sprintf("----%s avg boxy:%d,max boxy:%d,min boxy:%d", time_str,
			avg_boxy, max_boxy, min_boxy))
	}

	//blood pressure
	bpData := hisHealthData.GetBpData()
	if bpData != nil {
		sbp := bpData.GetSbp()
		dbp := bpData.GetDbp()
		log.Println(fmt.Sprintf("----%s sbp:%d,dbp:%d", time_str, sbp, dbp))
	}

	//hrv/pressure
	hrvData := hisHealthData.GetHrvData()
	if hrvData != nil {
		fatigue := int(hrvData.GetFatigue())
		if fatigue <= 0 {
			fatigue = int(math.Log(float64(hrvData.GetRMSSD())) * 20)
		}
		log.Println(fmt.Sprintf("----%s fatigue:%d", time_str, fatigue))
	}

	//temperature
	tmprData := hisHealthData.GetTemperatureData()
	if tmprData != nil {
		axillary_t := float32(tmprData.GetEstiArm()&0x0000ffff) / 100.0
		//commonly used
		est_t := float32((tmprData.GetEstiArm()>>16)&0x0000ffff) / 100.0
		shell_t := float32(tmprData.GetEviBody()&0x0000ffff) / 100.0
		env_t := float32((tmprData.GetEviBody()>>16)&0x0000ffff) / 100.0
		log.Println(fmt.Sprintf("----%s est_t:%f,shell_t:%f,env_t:%f,axillary_t:%f",
			time_str, est_t, shell_t, env_t, axillary_t))

	}
	//the sleep data here is not the final sleep result
	sleepData := hisHealthData.GetSleepData()
	if sleepData != nil {
		sleepDataList := sleepData.GetSleepData()
		charge := sleepData.GetCharge()
		shutdown := sleepData.GetShutDown()
		log.Println(fmt.Sprintf("----%s charge:%t,shutdown:%t,count:%d", time_str, charge,
			shutdown, len(sleepDataList)))
	}

	//bp hr
	bpBpmData := hisHealthData.GetBpBpmData()
	if bpBpmData != nil {
		bpm := bpBpmData.GetBpm()
		log.Println(fmt.Sprintf("----%s bp bpm:%d", time_str, bpm))
	}
}

func parseEcgData(hisEcgData *protobuf.HisDataECG) {
	seconds := int64(hisEcgData.GetTimeStamp().GetDateTime().GetSeconds())
	time_str := utils.ParsePbDateTime(seconds)
	rawEcgList := hisEcgData.GetRawData()
	log.Println(fmt.Sprintf("----%s count:%d", time_str, len(rawEcgList)))
}

func parseRriData(hisRriData *protobuf.HisDataRRI) {
	seconds := int64(hisRriData.GetTimeStamp().GetDateTime().GetSeconds())
	time_str := utils.ParsePbDateTime(seconds)
	rawRriList := hisRriData.GetRawData()
	rri_list := make([]int64, 0)
	for _, rawRri := range rawRriList {
		var value int64 = int64(rawRri)
		var fValue int64 = (value >> 16) & 0x0000ffff
		var sValue int64 = value & 0x0000ffff
		rri_list = append(rri_list, fValue)
		rri_list = append(rri_list, sValue)
	}
	log.Println(fmt.Sprintf("----%s count:%d", time_str, len(rri_list)))
}

func parseSpo2Data(hisSpo2Data *protobuf.HisDataSpo2) {
	seconds := int64(hisSpo2Data.GetTimeStamp().GetDateTime().GetSeconds())
	time_str := utils.ParsePbDateTime(seconds)
	rawSpo2List := hisSpo2Data.GetSpo2Data()
	for _, rawSpo2 := range rawSpo2List {
		spo2 := (rawSpo2 >> 24) & 0xFF
		hr := (rawSpo2 >> 16) & 0xFF
		perfusion := (rawSpo2 >> 8) & 0xFF
		touch := (rawSpo2) & 0xFF
		log.Println(fmt.Sprintf("----%s spo2:%d,hr:%d,perfusion:%d,touch:%d",
			time_str, spo2, hr, perfusion, touch))
	}
}

func parseThirdPartyData(hisThirdPartyData *protobuf.HisDataThirdParty) {
	thirdPartyHealth := hisThirdPartyData.GetDataHealth()
	if thirdPartyHealth != nil {
		mac_addr := thirdPartyHealth.GetMacAddr()
		log.Println(fmt.Sprintf("----3rd party device mac address:%s", mac_addr))
		bpData := thirdPartyHealth.GetBpData()
		if bpData != nil {
			seconds := int64(bpData.GetTime().GetDateTime().GetSeconds())
			time_str := utils.ParsePbDateTime(seconds)
			sbp := bpData.GetSbp()
			dbp := bpData.GetDbp()
			hr := bpData.GetHr()
			pulse := bpData.GetPulse()
			//mode 0 manual, 1 auto
			mode := bpData.GetMODE()
			log.Println(fmt.Sprintf("----3rd party %s sbp:%d,dbp:%d,hr:%d,pulse:%d,mode:%d", time_str,
				sbp, dbp, hr, pulse, mode))
		}

		gluData := thirdPartyHealth.GetGluData()
		if gluData != nil {
			seconds := int64(gluData.GetTime().GetDateTime().GetSeconds())
			time_str := utils.ParsePbDateTime(seconds)
			blood_glu := gluData.GetGlu()
			log.Println(fmt.Sprintf("----3rd party %s blood glucose:%d", time_str, blood_glu))
		}

		scaleData := thirdPartyHealth.GetScaleData()
		if scaleData != nil {
			seconds := int64(scaleData.GetTime().GetDateTime().GetSeconds())
			time_str := utils.ParsePbDateTime(seconds)
			weight := scaleData.GetWeight()
			impedance := scaleData.GetImpedance()
			uints := scaleData.GetUints()
			bodyfat := scaleData.GetBodyFatPercentage()
			log.Println(fmt.Sprintf("----3rd party %s weight:%d, impedance:%d, uints:%d, body_fat_percentage:%d",
				time_str, weight, impedance, uints, bodyfat))
		}

		spo2Data := thirdPartyHealth.GetSpo2Data()
		if spo2Data != nil {
			seconds := int64(spo2Data.GetTime().GetDateTime().GetSeconds())
			time_str := utils.ParsePbDateTime(seconds)
			spo2 := spo2Data.GetSpo2()
			bpm := spo2Data.GetBpm()
			pi := spo2Data.GetPi()
			log.Println(fmt.Sprintf("----3rd party %s spo2:%d,hr:%d,pi:%d", time_str,
				spo2, bpm, pi))
		}

		tmprData := thirdPartyHealth.GetTempData()
		if tmprData != nil {
			seconds := int64(tmprData.GetTime().GetDateTime().GetSeconds())
			time_str := utils.ParsePbDateTime(seconds)
			tmpr := tmprData.GetBodyTemp()
			log.Println(fmt.Sprintf("----3rd party %s temperature:%d", time_str, tmpr))
		}

		bkData := thirdPartyHealth.GetBloodKetonesData()
		if bkData != nil {
			seconds := int64(bkData.GetTime().GetDateTime().GetSeconds())
			time_str := utils.ParsePbDateTime(seconds)
			bk_val := bkData.GetBloodKetones()
			log.Println(fmt.Sprintf("----3rd party %s blood ketones:%d", time_str, bk_val))
		}

		uaData := thirdPartyHealth.GetUricAcidData()
		if uaData != nil {
			seconds := int64(uaData.GetTime().GetDateTime().GetSeconds())
			time_str := utils.ParsePbDateTime(seconds)
			ua_val := uaData.GetUricAcid()
			log.Println(fmt.Sprintf("----3rd party %s uric acid:%d", time_str, ua_val))
		}
	}
}

func parsePpgData(hisPpgData *protobuf.HisDataPPG) {
	seconds := int64(hisPpgData.GetTimeStamp().GetDateTime().GetSeconds())
	time_str := utils.ParsePbDateTime(seconds)
	rawPpgList := hisPpgData.GetRawData()
	ppg_list := make([]int16, 0)
	for _, rawPpg := range rawPpgList {
		var value = rawPpg
		var fValue int16 = int16((value >> 16) & 0x0000ffff)
		if fValue < 0 {
			fValue = fValue * -1
		}
		var sValue int16 = int16(value & 0x0000ffff)
		if sValue < 0 {
			sValue = sValue * -1
		}
		ppg_list = append(ppg_list, fValue)
		ppg_list = append(ppg_list, sValue)
		log.Println(fmt.Sprintf("%s ppg:%d,%d", time_str, fValue, sValue))
	}
}

func parseAccData(hisAccData *protobuf.HisACCelerometer) {
	seconds := int64(hisAccData.GetTimeStamp().GetDateTime().GetSeconds())
	time_str := utils.ParsePbDateTime(seconds)
	xBytes := hisAccData.GetAccX()
	xList := parseBytesString(xBytes)

	yBytes := hisAccData.GetAccY()
	yList := parseBytesString(yBytes)

	zBytes := hisAccData.GetAccZ()
	zList := parseBytesString(zBytes)

	for i := 0; i < len(xList); i++ {
		log.Println(fmt.Sprintf("%s acc x:%d,y:%d,z:%d", time_str, xList[i],
			yList[i], zList[i]))
	}
}

func parseMultiLeadsEcgData(hisEcgData *protobuf.HisDataMultiLeadsECG) {
	seconds := int64(hisEcgData.GetTimeStamp().GetDateTime().GetSeconds())
	time_str := utils.ParsePbDateTime(seconds)
	channel := int(*hisEcgData.NumberOfChannels)
	single_channel_length := int(*hisEcgData.SingleDataByteLen)
	log.Println(fmt.Sprintf("%s channel_num x:%d,single_data_len:%d", time_str,
		channel, single_channel_length))
	buffer := hisEcgData.GetRawData()
	unit_size := channel * single_channel_length
	for i := unit_size; i <= len(buffer); i += unit_size {
		for j := 0; j < channel; j++ {
			num := 0
			for k := 0; k < single_channel_length; k++ {
				pos := i - single_channel_length*(channel-j) + k
				offset := 8 * (single_channel_length - k - 1)
				part := int(buffer[pos]) << offset
				num += part
			}
			log.Println(fmt.Sprintf("channel %d, val: %d", j+1, num))
		}
	}
}

func parseBytesString(bytesArr []byte) []int {
	vlist := make([]int, 0)
	for i := 1; i < len(bytesArr); i += 2 {
		var low byte = bytesArr[i-1]
		var high int = int(bytesArr[i] << 8)
		var real_val int = int(low) + high
		vlist = append(vlist, real_val)
	}
	return vlist
}
